
#include "vtkConstants.hpp"
#include "vtkFunctions.hpp"
#include "vtkFunctions_export.hpp"

namespace hms::vtk
{

template<typename SolverType>
void writeBoundaryCondition(
	xml::XMLElement* patchNode,
	const BoundaryPatch& patch,
	const SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::FieldName fieldName
){
	using MeshType = typename SolverBase<SolverType>::MeshType;

	const Field<MeshType>& field { solver.field(fieldName) };
	const BoundaryCondition<MeshType>* bc { &( field.boundaryCondition(patch) ) };

	xml::XMLElement* bcElem { patchNode->InsertNewChildElement("BoundaryCondition") };

	bcElem->SetAttribute( "Field",
		std::string{ magic_enum::enum_name(fieldName) }.c_str()
	);
	bcElem->SetAttribute( "Type",
		std::string{ magic_enum::enum_name( bc->type() ) }.c_str()
	);

	if ( auto ptr = dynamic_cast<const ZeroGradient<MeshType>*>(bc) ){
		return;
	} else if ( auto ptr = dynamic_cast<const StaticValue<MeshType>*>(bc) ){
		writeBoundaryCondition( bcElem, ptr );
	} else if ( auto ptr = dynamic_cast<const DynamicValue<MeshType>*>(bc) ){
		writeBoundaryCondition( bcElem, ptr );
	} else if ( std::is_same_v<SolverType, SWESolver<MeshType>> ){
		writeSWEBoundaryCondition( bcElem, bc );
	} else {
		assert( false && "Boundary condition type not implemented!" );
	}
}

/**
 * @brief Writes the boundary conditions of one field to an xml document.
 * Use this overload if you want to update the boundary conditions of a field.
 * 
 * @tparam SolverType 
 * @param root 
 * @param solver 
 * @param fieldName 
 */
template<typename SolverType>
void writeBoundaryConditions(
	xml::XMLNode* root,
	const SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::FieldName fieldName
){
	const std::vector<BoundaryPatch>& patches { solver.mesh().boundaryPatches() };
	std::vector<xml::XMLElement*> patchNodes { internal::boundaryPatchNodes(
		root, patches
	)};
		
	for ( size_t j{0}; j < patches.size(); ++j ){
		/* delete old boundary condition node, if it exists */
		xml::XMLElement* bcElem { internal::getChildNodeByAttributeValue(
			patchNodes[j], "BoundaryCondition", "Field",
			std::string{ magic_enum::enum_name(fieldName) }.c_str()
		) };
		if ( bcElem ){
			patchNodes[j]->DeleteChild(bcElem);
		}
		writeBoundaryCondition<SolverType>(
			patchNodes[j],
			patches[j],
			solver,
			fieldName
		);
	}
}



template<typename SolverType>
void writeBoundaryConditions(
	xml::XMLNode* root,
	const SolverBase<SolverType>& solver
){
	using S = SolverBase<SolverType>;
	using FieldName = typename S::FieldName;

	const std::vector<BoundaryPatch>& patches { solver.mesh().boundaryPatches() };
	std::vector<xml::XMLElement*> patchNodes { internal::boundaryPatchNodes(
		root, patches
	)};

	for ( int i{0}; i < S::nFields ; ++i ){
		FieldName fieldName { static_cast<FieldName>(i) };
		
		for ( size_t j{0}; j < patches.size(); ++j ){
			writeBoundaryCondition<SolverType>(
				patchNodes[j],
				patches[j],
				solver,
				fieldName
			);
		}
	}
}

} // namespace hms::vtk
